<template>
    <div class="VaseViewer" :ref="three.el">
        <div class="container-rb">{{ 'xx' }}</div>
        <div class="container-lb">
            <a-button @click="actions.clear" size="small" type="primary">清空</a-button>
        </div>
    </div>
</template>

<script setup>
    import { ref, reactive, onMounted, onUnmounted, watch } from 'vue';
    import * as THREE from 'three';
    import Stats from 'three/examples/jsm/libs/stats.module.js';
    import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

    const props = defineProps({
        hullPoints: { type: Array, default: [], required: false },
        finalThickness: { type: Number, default: 1, required: false },
        postProcess: { type: String, default: '', required: false } // 后处理
    });

    // https://stackoverflow.com/questions/29884485/threejs-canvas-size-based-on-container
    function getDomSize(dom) {
        return { width: dom.parentNode.clientWidth, height: dom.clientHeight };
    }

    const emit = defineEmits(['downloadProgress']);

    let requestId = null;
    let orbitControls = null;

    const three = {
        el: ref(),
        scene: null,
        camera: null,
        renderer: null,
        canvasWidth: 0,
        canvasHeight: 0,
        resizeObserver: null, // 监听 canvas 大小变化
        stats: null, // 帧率监测
        vaseShellGroup: new THREE.Group(), // 用于添加圆球（表示点）和线
        vasePlateGroup: new THREE.Group(), // 用于添加封闭折线
        plateGroup: new THREE.Group() // 用于添加打印板
    };

    // 添加打印底板
    const addPlate = () => {
        const generatePlate = () => {
            const length = 200;
            const plate = new THREE.Group();

            // gap=10 的网格
            const color = 0xaaaaaa;
            const divisions = length / 10;
            const gridHelper = new THREE.GridHelper(length, divisions, color, color); // xz 平面
            gridHelper.rotation.x = Math.PI / 2;
            plate.add(gridHelper);

            return plate;
        };

        const plate = generatePlate();
        three.plateGroup.add(plate);
    };

    const addAxes = () => {
        const length = 130;
        const origin = new THREE.Vector3(0, 0, 0);
        const infos = [
            // x
            { dir: new THREE.Vector3(1, 0, 0), color: 0xff0000 },
            // y
            { dir: new THREE.Vector3(0, 1, 0), color: 0x00ff00 }
            // z
            // { dir: new THREE.Vector3(0, 0, 1), color: 0x0000ff }
        ];
        infos.forEach(({ dir, color }) => {
            const arrowHelper = new THREE.ArrowHelper(dir, origin, length, color);

            // 显示在最上面：解决和其他 line 重合引起 z-fighting
            arrowHelper.cone.material.depthTest = false;
            arrowHelper.line.material.depthTest = false;

            // 增加立体感
            arrowHelper.cone.material = new THREE.MeshStandardMaterial({
                color,
                metalness: 0.6,
                roughness: 0.3
            });

            three.scene.add(arrowHelper);
        });
    };

    const state = reactive({
        points: []
    });

    onMounted(() => {
        // setup
        actionsThree.setup();
        actionsThree.setupGroup();
        // actionsThree.setupStats();
        actionsThree.setupResizeListener();

        // three.scene.add(new THREE.HemisphereLight(0xffffff, 0x222222, 1.75));
        orbitControls = new OrbitControls(three.camera, three.renderer.domElement);

        actionsThree.animate();

        addPlate(); // 添加网格
        addAxes(); // 添加坐标轴

        addVase();

        watch(
            () => [props.hullPoints, props.finalThickness, props.postProcess],
            ([newHullPoints, newFinalThickness], [oldHullPoints, oldFinalThickness]) => {
                // console.log('新的 hullPoints:', newHullPoints);
                // console.log('新的 finalThickness:', newFinalThickness);

                // 花瓶底座
                // const vasePlate = generatePlate();
                // three.vasePlateGroup.clear();
                // three.vasePlateGroup.add(vasePlate);

                addVase();
                // 花瓶外壳
                // generateVaseShell();
            },
            { immediate: false }
        );
    });

    function generateVaseShell() {
        // 定义花瓶轮廓（z 方向是高度，x 是半径）
        const points = [];
        points.push(new THREE.Vector2(0, 0)); // 底部中心
        points.push(new THREE.Vector2(10, 0)); // 底部半径
        points.push(new THREE.Vector2(15, 20)); // 中间膨胀
        points.push(new THREE.Vector2(8, 40)); // 上部收缩
        points.push(new THREE.Vector2(0, 50)); // 顶部中心

        const geometry = new THREE.LatheGeometry(points, 64);
        // 64：旋转分段数，越大越平滑

        const material = new THREE.MeshStandardMaterial({
            color: 0xffaa00,
            metalness: 0.3,
            roughness: 0.6,
            side: THREE.DoubleSide
        });

        const mesh = new THREE.Mesh(geometry, material);

        three.vaseShellGroup.clear();
        three.vaseShellGroup.add(mesh);
    }

    function extrudeAlongCurveFlat(hullPoints, curve, steps) {
        const vertices = [];
        const indices = [];
        const n = hullPoints.length;

        for (let i = 0; i <= steps; i++) {
            const t = i / steps;
            const center = curve.getPoint(t); // 曲线上当前点
            // 将 hullPoints 平移到 center，保持水平
            hullPoints.forEach((p) => {
                vertices.push(center.x + p.x, center.y + p.y, center.z);
            });
        }

        // 生成侧面索引
        for (let i = 0; i < steps; i++) {
            const offset = i * n;
            for (let j = 0; j < n; j++) {
                const a = offset + j;
                const b = offset + ((j + 1) % n);
                const c = offset + n + ((j + 1) % n);
                const d = offset + n + j;

                // 两个三角形组成矩形面
                indices.push(a, b, d);
                indices.push(b, c, d);
            }
        }

        // 可选：生成顶面和底面
        // 底面
        for (let j = 1; j < n - 1; j++) {
            indices.push(0, j, j + 1);
        }
        // 顶面
        const topOffset = steps * n;
        for (let j = 1; j < n - 1; j++) {
            indices.push(topOffset, topOffset + j + 1, topOffset + j);
        }

        // 构建 BufferGeometry
        const geometry = new THREE.BufferGeometry();
        geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
        geometry.setIndex(indices);
        geometry.computeVertexNormals();

        return geometry;
    }

    // const addVase = () => {
    //     const points = [
    //         new THREE.Vector2(0, 0), // 底部中心
    //         new THREE.Vector2(20, 0), // 底部半径
    //         new THREE.Vector2(15, 20), // 中间膨胀
    //         new THREE.Vector2(8, 40) // 上部收缩
    //         // new THREE.Vector2(0, 50) // 顶部
    //     ];

    //     const geometry = new THREE.LatheGeometry(points, 64); // 64 分段
    //     const material = new THREE.MeshStandardMaterial({
    //         color: 0x00ff00,
    //         metalness: 0.3,
    //         roughness: 0.6,
    //         side: THREE.DoubleSide
    //     });
    //     const mesh = new THREE.Mesh(geometry, material);

    //     // 线框（覆盖在上面）
    //     const wireframe = new THREE.LineSegments(
    //         new THREE.WireframeGeometry(geometry),
    //         new THREE.LineBasicMaterial({ color: 0x000000 })
    //     );
    //     mesh.add(wireframe);

    //     mesh.rotation.x = Math.PI / 2;

    //     three.scene.add(mesh);
    // };

    const postProcessGeometry = (geometry) => {
        switch (props.postProcess) {
            case '截面形状-波形': {
                break;
            }
        }
    };

    const addVase = () => {
        console.log('add vase: ', props.postProcess);
        // 创建基础点（注意：Vector2 的 x 建议设为非负且合理范围，避免旋转后异常，这里简单调整了 x 为非负 ）
        const rawPoints = [
            // new THREE.Vector2(0, 0), // 底部中心，旋转轴位置
            new THREE.Vector2(50, 0), // 底部外缘，决定底部宽度
            new THREE.Vector2(60, 61), // 底部向上缓慢收窄
            new THREE.Vector2(25, 153), // 继续收窄，过渡到鼓腹部分
            new THREE.Vector2(50, 180) // 开始向外鼓，形成腹部
            // new THREE.Vector2(20, 50), // 腹部最鼓的位置
            // new THREE.Vector2(18, 60), // 向上收窄，过渡到颈部
            // new THREE.Vector2(15, 70), // 颈部收窄
            // new THREE.Vector2(16, 80), // 瓶口轻微外扩
            // new THREE.Vector2(14, 85) // 瓶口位置，可微调让轮廓更贴合
        ];

        // 使用样条曲线（Catmull-Rom spline）进行平滑插值
        const curve = new THREE.SplineCurve(rawPoints);
        const points = curve.getPoints(100); // 取 100 个平滑点，数值越大曲线越平滑（性能开销也会增加）

        // 用 LatheGeometry 旋转生成花瓶
        const geometry = new THREE.LatheGeometry(points, 256); // 分段越多越圆滑

        geometry.computeVertexNormals();

        switch (props.postProcess) {
            case '截面形状-多边形': {
                console.log('111: ', props.postProcess);
                // 🌊 2️⃣ 顶点波动：让外壳产生周期性起伏
                const pos = geometry.attributes.position;
                const norm = geometry.attributes.normal;
                const v = new THREE.Vector3();
                const n = new THREE.Vector3();

                const amplitude = 0.8; // 波动振幅（越大越明显）
                const freqAround = 12; // 沿圆周的波动次数
                const freqHeight = 0.2; // 沿高度方向的波动频率

                for (let i = 0; i < pos.count; i++) {
                    v.fromBufferAttribute(pos, i);
                    n.fromBufferAttribute(norm, i);

                    // 获取当前顶点的极坐标角度与高度
                    const angle = Math.atan2(v.z, v.x);
                    const height = v.y;

                    // 波动公式（圆周+高度混合波）
                    const offset = Math.sin(angle * freqAround + height * freqHeight) * amplitude;

                    // 沿法线方向偏移
                    v.addScaledVector(n, offset);

                    pos.setXYZ(i, v.x, v.y, v.z);
                }

                pos.needsUpdate = true;
                geometry.computeVertexNormals();
                break;
            }
            case '截面形状-波形': {
                // 🌊 将圆形花瓶改为 12 边形（平滑柔和版）
                const pos = geometry.attributes.position;
                const norm = geometry.attributes.normal;
                const v = new THREE.Vector3();

                // 控制参数
                const sides = 12; // 边数
                const sharpness = 1.0; // 边角清晰度
                const strength = 1 / 3; // 🌸 控制变化幅度（1/3 表示更圆滑）

                for (let i = 0; i < pos.count; i++) {
                    v.fromBufferAttribute(pos, i);

                    // 计算当前顶点的角度（绕 y 轴）
                    const angle = Math.atan2(v.z, v.x);

                    // 将圆的半径按角度压缩成近似 12 边形外形
                    const sectorAngle = (2 * Math.PI) / sides;
                    const nearestCorner = Math.round(angle / sectorAngle) * sectorAngle;

                    // 原始“边角形变系数”
                    const delta = Math.cos(((angle - nearestCorner) * sides) / 2) ** sharpness;

                    // 基础半径
                    const radius = Math.sqrt(v.x * v.x + v.z * v.z);

                    // 平滑控制：从圆形到多边形的过渡（strength 越小越圆）
                    const newRadius = radius * (1 - strength + delta * strength);

                    // 根据新半径更新坐标
                    v.x = Math.cos(angle) * newRadius;
                    v.z = Math.sin(angle) * newRadius;

                    pos.setXYZ(i, v.x, v.y, v.z);
                }

                pos.needsUpdate = true;
                geometry.computeVertexNormals();
                break;
            }
        }

        const material = new THREE.MeshNormalMaterial({
            side: THREE.DoubleSide,
            flatShading: false // 平滑过渡更自然
        });

        const mesh = new THREE.Mesh(geometry, material);
        mesh.scale.set(0.5, 0.5, 0.5);
        // 线框覆盖
        const wireframe = new THREE.LineSegments(
            new THREE.WireframeGeometry(geometry),
            new THREE.LineBasicMaterial({ color: 0x000000 })
        );
        // mesh.add(wireframe);

        mesh.rotation.x = Math.PI / 2;

        three.vaseShellGroup.clear();
        three.vaseShellGroup.add(mesh);
    };

    const addVase2 = () => {
        console.log('addVase();');
        const points = [
            new THREE.Vector2(0, 0), // 底部中心
            new THREE.Vector2(20, 0), // 底部半径
            new THREE.Vector2(15, 20), // 中间膨胀
            new THREE.Vector2(8, 40) // 上部收缩
            // new THREE.Vector2(0, 50) // 顶部（可选）
        ];

        // 🌸 1️⃣ 创建基本花瓶几何
        const geometry = new THREE.LatheGeometry(points, 128);

        const pos = geometry.attributes.position;
        const norm = geometry.attributes.normal;
        const v = new THREE.Vector3();
        const n = new THREE.Vector3();

        const amplitude = 0.8; // 波动振幅（越大越明显）
        const freqAround = 12; // 沿圆周的波动次数
        const freqHeight = 0.2; // 沿高度方向的波动频率

        for (let i = 0; i < pos.count; i++) {
            v.fromBufferAttribute(pos, i);
            n.fromBufferAttribute(norm, i);

            // 获取当前顶点的极坐标角度与高度
            const angle = Math.atan2(v.z, v.x);
            const height = v.y;

            // 波动公式（圆周+高度混合波）
            const offset = Math.sin(angle * freqAround + height * freqHeight) * amplitude;

            // 沿法线方向偏移
            v.addScaledVector(n, offset);

            pos.setXYZ(i, v.x, v.y, v.z);
        }

        pos.needsUpdate = true;
        geometry.computeVertexNormals();

        postProcessGeometry(geometry);

        // 🎨 3️⃣ 材质与网格
        const material = new THREE.MeshStandardMaterial({
            color: 0x00ff00,
            metalness: 0.3,
            roughness: 0.6,
            side: THREE.DoubleSide
        });

        const mesh = new THREE.Mesh(geometry, material);

        // 线框覆盖
        const wireframe = new THREE.LineSegments(
            new THREE.WireframeGeometry(geometry),
            new THREE.LineBasicMaterial({ color: 0x000000 })
        );
        mesh.add(wireframe);

        mesh.rotation.x = Math.PI / 2;

        three.vaseShellGroup.add(mesh);
    };

    onUnmounted(() => {
        actionsThree.dispose();
    });

    const generatePlate = () => {
        const { hullPoints, finalThickness } = props;
        // 1. 构建 THREE.Shape
        const shape = new THREE.Shape();
        hullPoints.forEach((p, i) => {
            if (i === 0) shape.moveTo(p.x, p.y);
            else shape.lineTo(p.x, p.y);
        });
        shape.closePath(); // 闭合轮廓

        // 2. 创建 ExtrudeGeometry
        const geometry = new THREE.ExtrudeGeometry(shape, {
            depth: finalThickness, // 拉伸高度
            bevelEnabled: false // 关闭斜角
        });

        // 3. 创建 Mesh
        const material = new THREE.MeshStandardMaterial({
            color: 0x00ff00,
            metalness: 0.3,
            roughness: 0.6,
            side: THREE.DoubleSide
        });
        const mesh = new THREE.Mesh(geometry, material);

        // 4. 默认 Extrude 沿 +Z 方向，如果你想从 z=0 开始，可以调整 position
        mesh.position.z = 0;

        return mesh;
    };

    const actionsThree = {
        setup() {
            const { width, height } = getDomSize(three.el.value);

            three.canvasWidth = width;
            three.canvasHeight = height;

            // 透视相机
            three.camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
            three.camera.position.set(0, -320, 100);
            three.camera.lookAt(0, 0, 0);

            three.renderer = new THREE.WebGLRenderer({
                antialias: true,
                alpha: true,
                logarithmicDepthBuffer: true
            });
            three.renderer.setPixelRatio(window.devicePixelRatio); // 显示模糊，要设置
            three.renderer.setSize(width, height);
            three.renderer.setClearColor(0xdddddd, 1);
            // renderer.outputEncoding = THREE.sRGBEncoding;
            three.renderer.autoClear = false;

            three.scene = new THREE.Scene();
            three.scene.background = new THREE.Color(0xdddddd);
            three.scene.add(three.camera);

            three.el.value.appendChild(three.renderer.domElement);

            // light
            let ambit = new THREE.AmbientLight(0xffffff, 2.8);
            three.scene.add(ambit);

            const light = new THREE.DirectionalLight(0xffffff, 0.5);
            light.position.set(0, 1, 0);
            three.scene.add(light);

            const directionalLight = new THREE.DirectionalLight(0xffffff, 0.25);
            directionalLight.position.set(0, 0, 1);
            three.scene.add(directionalLight);
        },
        setupGroup() {
            three.scene.add(three.vaseShellGroup);
            three.scene.add(three.vasePlateGroup);
            three.scene.add(three.plateGroup);
        },
        setupStats() {
            // 创建性能监视器
            three.stats = new Stats();
            // 设置监视器面板，传入面板id（0: fps, 1: ms, 2: mb）
            three.stats.setMode(0);
            // 设置监视器位置
            three.stats.domElement.style.position = 'absolute';
            three.stats.domElement.style.width = '80px';
            three.stats.domElement.style.height = '48px';
            three.stats.domElement.style.left = '5px';
            three.stats.domElement.style.top = '5px';
            // 将监视器添加到页面中
            three.el.value.appendChild(three.stats.domElement);
        },
        animate() {
            requestId = requestAnimationFrame(actionsThree.animate);
            // three.renderer.clear(true, true, true);
            three.renderer.render(three.scene, three.camera);
            // three.stats?.update();
        },
        resizeWindow() {
            if (!three.el.value) return;
            const { width, height } = getDomSize(three.el.value);
            // console.log('resizeWindow: ', width, height);
            if (width * height !== 0) {
                three.canvasWidth = width;
                three.canvasHeight = height;
                three.renderer.setSize(width, height, true);
                // 正交相机自适应 resize：https://blog.csdn.net/yinge0508/article/details/123114184
                three.camera.aspect = width / height;
                three.camera.updateProjectionMatrix();
                // three.camera.lookAt(0, 0, 0);
            }
        },
        setupResizeListener() {
            three.resizeObserver = new ResizeObserver((entries) => {
                for (const entry of entries) {
                    actionsThree.resizeWindow();
                }
            });
            three.resizeObserver.observe(three.el.value);
            // resize 存在 bug: https://stackoverflow.com/questions/29884485/threejs-canvas-size-based-on-container
            window.addEventListener('resize', actionsThree.resizeWindow, true);
        },
        dispose() {
            requestId && cancelAnimationFrame(requestId);
            three.resizeObserver.disconnect();
            window.removeEventListener('resize', actionsThree.resizeWindow, true);
            // 释放 webgl context
            three.renderer.dispose();
            three.renderer.forceContextLoss();
            three.renderer.context = null;
            three.renderer.domElement = null;
            three.renderer = null;
        }
    };

    const actions = {
        clear() {
            state.points = [];
            three.vaseShellGroup.clear();
            three.vasePlateGroup.clear();
        }
    };
</script>

<style lang="scss" scoped>
    .VaseViewer {
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;

        .container-rb {
            position: absolute;
            right: 5px;
            bottom: 5px;
            color: black;
        }

        .container-lb {
            position: absolute;
            bottom: 0;
            left: 0;
            padding: 3px;
        }
    }
</style>
